package ez

import (
	"bytes"
	"fmt"
	"gitee.com/dreamwood/ez-go/tools"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"
)

// 文件日志
type FileLogger struct {
	Prefix     string
	TraceDepth int    //跟踪起始深度
	DumpDepth  int    //追踪深度
	FilePath   string //文件名，默认“ez.log”
	targetFile *os.File
	MaxSize    int64 //最大容量，默认2M
	curLine    int64 //当前行数
	curSize    int64
	// UseBuffer 是否延迟写入
	UseBuffer     bool
	WriteInterval time.Duration
	buffers       *bytes.Buffer
	lock          sync.Mutex
	fileIndex     int
	IsPlain       bool //干净模式
}

func NewFileLogger() *FileLogger {
	fl := &FileLogger{
		FilePath:      "./var/logs/ez.log",
		MaxSize:       1 * 1024 * 1024,
		Prefix:        "[EZ]",
		WriteInterval: 2 * time.Second,
		buffers:       bytes.NewBufferString(fmt.Sprintf("======FileLogger Starts At %s======", time.Now().Format(time.RFC1123Z))),
		lock:          sync.Mutex{},
		TraceDepth:    1,
		DumpDepth:     0,
	}
	return fl
}

func (this *FileLogger) prepare() {
	//检查文件是否存在
	if tools.FileExist(this.FilePath) {
		//更新
		file, e := os.OpenFile(this.FilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
		PrintError(e)
		this.targetFile = file
	} else {
		_ = tools.CreateDirForPath(this.FilePath)
		this.createNewFile()
	}
	if this.UseBuffer {
		go this.writeBuffersToFile()
	}

}

func (this *FileLogger) updateFileSize() bool {
	this.lock.Lock()
	defer this.lock.Unlock()
	info, e := this.targetFile.Stat()
	if e != nil {
		PrintError(e)
		return true
	}
	this.curSize = info.Size()
	return this.curSize > this.MaxSize
}

func (this *FileLogger) writeBuffersToFile() {
	if this.targetFile == nil {
		this.prepare()
	}
	for {
		time.Sleep(this.WriteInterval)
		//写入数据
		if this.buffers.Len() > 0 {
			this.lock.Lock()
			_, e := this.targetFile.Write(this.buffers.Bytes())
			this.buffers.Reset()
			PrintError(e)
			this.lock.Unlock()
			//写入完成之后检查是否需要创建新的文件
			if this.updateFileSize() {
				this.createNewFile()
			}
		}
	}
}

func (this *FileLogger) Write(data string) {
	if !this.IsPlain {
		pc, f, l, _ := runtime.Caller(this.TraceDepth)
		data = fmt.Sprintf("%s %s %s:%d\n%s %s FUNC:%s\n%s\n\n",
			this.Prefix,
			tools.GetDateYMDHIS("-", ":", " "),
			f, l,

			this.Prefix,
			tools.GetDateYMDHIS("-", ":", " "),
			runtime.FuncForPC(pc).Name(),
			data,
		)
		rightArrow := ""
		for i := 0; i < this.DumpDepth; i++ {
			pc, f, l, _ = runtime.Caller(this.TraceDepth + i + 1)
			if f == "" {
				break
			}
			rightArrow += "→"
			data = fmt.Sprintf("%s %s %s %s:%d\n%s %s FUNC:%s\n",
				this.Prefix,
				tools.GetDateYMDHIS("-", ":", " "),
				rightArrow,
				f, l,
				this.Prefix,
				tools.GetDateYMDHIS("-", ":", " "),
				runtime.FuncForPC(pc).Name()) + data
		}
	} else {
		data = fmt.Sprintf("%s %s \n",
			this.Prefix,
			data,
		)
	}

	if this.targetFile == nil {
		this.prepare()
	}
	if this.UseBuffer {
		this.lock.Lock()
		this.buffers.WriteString(data)
		this.lock.Unlock()
	} else {
		this.lock.Lock()
		_, e := this.targetFile.WriteString(data)
		PrintError(e)
		this.lock.Unlock()
		//写入完成之后检查是否需要创建新的文件
		if this.updateFileSize() {
			this.createNewFile()
		}
	}

}

func (this *FileLogger) createNewFile() {
	this.lock.Lock()
	defer this.lock.Unlock()
	if this.targetFile != nil {
		if e := this.targetFile.Close(); e != nil {
			PrintError(e)
		} else {
			dateInfo := tools.GetDateYMDHIS("_", "_", "_")
			newPath := strings.ReplaceAll(this.FilePath, ".log", fmt.Sprintf("_%s_%d.log", dateInfo, this.fileIndex))
			if err := os.Rename(this.FilePath, newPath); err != nil {
				PrintError(err)
			}
			this.fileIndex++
		}
	}
	//absPath, _ := filepath.Abs(this.FilePath)
	_ = tools.CreateDirForPath(this.FilePath)
	file, e := os.OpenFile(this.FilePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0755)
	PrintError(e)
	this.targetFile = file
}
